package com.ruiysoft.crm.log;

import cn.taroco.common.entity.SysLog;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.ruiysoft.crm.feign.SysLogService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.io.InputStreamSource;
import org.springframework.stereotype.Component;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.util.StopWatch;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Date;

/**
 * @author fanyl5
 */
@Slf4j
@Aspect
@Component
public class LogsAspect {


    private final SysLogService sysLogService;

    private static final String MSG = "\n--请求源IP：{}\n --请求URL：{}\n --请求类型：{}\n --类位置：{}\n --请求方法：{}\n --请求参数：{}\n --请求结果：{}\n --响应时间：{} ms";

    public LogsAspect(SysLogService sysLogService) {
        this.sysLogService = sysLogService;
    }

    @Pointcut("@annotation(com.ruiysoft.crm.log.InvokeLog)")
    public void log() {

    }

    @Around("log()")
    public Object printLog(ProceedingJoinPoint joinPoint) throws IOException {
        StopWatch clock = new StopWatch();
        clock.start();

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

        String methodName = joinPoint.getSignature().getName();
        String className = joinPoint.getTarget().getClass().getName();
        //IP地址
        String ipAddr = getRemoteHost(request);
        String url = request.getRequestURL().toString();
        String httpMethod = request.getMethod();
        String requestParam = preHandle(joinPoint);
        //操作人
        String userName = request.getHeader("x-user-header");
        String exceptionMsg = "";
        Object result = null;
        long totalTime;
        try {
            result = joinPoint.proceed();
        } catch (Throwable throwable) {
            exceptionMsg = throwable.getMessage();
            result = throwable.getMessage();
            log.error("获取结果失败{}", throwable.getMessage());
        } finally {
            clock.stop();
            totalTime = clock.getTotalTimeMillis();
        }
        log.info(MSG, ipAddr, url, httpMethod, className, methodName, requestParam, postHandle(result), totalTime);
        addLog(userName, ipAddr, url, httpMethod, requestParam, postHandle(result), totalTime, exceptionMsg);
        return result;
    }

    private void addLog(String userName, String ipAddr, String url, String httpMethod, String params, String result, long totalTime, String exceptionMsg) {
        SysLog sysLog = new SysLog();
        sysLog.setType("Operation");
        sysLog.setTitle("Operation");
        sysLog.setCreateBy(userName);
        sysLog.setCreateTime(new Date());
        sysLog.setUpdateTime(new Date());
        sysLog.setRemoteAddr(ipAddr);
        sysLog.setRequestUri(url);
        sysLog.setMethod(httpMethod);
        sysLog.setParams(params);
        sysLog.setTime(totalTime);
        if (StringUtils.hasText(exceptionMsg)) {
            sysLog.setException(exceptionMsg);
        }

        //sysLogService.add(sysLog);
    }

    /**
     * 返回数据
     *
     * @param retVal
     * @return
     */
    private String postHandle(Object retVal) {
        if (null == retVal) {
            return "";
        }
        return JSON.toJSONString(retVal);
    }

    /**
     * 入参数据
     *
     * @param joinPoint
     * @return
     */
    private String preHandle(ProceedingJoinPoint joinPoint) throws IOException {

        String reqParam = "";
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method targetMethod = methodSignature.getMethod();
        Annotation[] annotations = targetMethod.getAnnotations();
        for (Annotation annotation : annotations) {
            if (annotation.annotationType().equals(InvokeLog.class)) {
                reqParam = JSON.toJSONString(argsDemote(joinPoint.getArgs()));
                break;
            }
        }
        return reqParam;
    }

    private JSONArray argsDemote(Object[] args) {
        JSONArray param = new JSONArray();
        if (args == null || args.length == 0) {
            return new JSONArray();
        }
        Object[] params = new Object[args.length];
        for (int i = 0; i < args.length; i++) {
            Object arg = args[i];
            if (arg instanceof ServletRequest || arg instanceof ServletResponse
                    || arg instanceof ModelMap || arg instanceof Model
                    || arg instanceof InputStreamSource || arg instanceof File) {
                params[i] = args.toString();
            } else {
                params[i] = args[i];
            }
            param.add(params[i]);
        }
        return param;
    }

    /**
     * 获取目标主机的ip
     *
     * @param request
     * @return 目标主机IP
     */
    private String getRemoteHost(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
    }
}
